﻿using ConsoleApp2.util;
using FCN;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ConsoleApp2.Layer
{
    class LstmGRUcell
    {
        int input_size;
        int hidden_size;
        int kernel_size;
        public convlayer ConvGates;
        public int _stride = 1;
        public int _padding = 0;
        public int input_sizeNUM { get; set; }
        public int output_sizenum { get; set; }
        public LstmGRUcell(int _input_size, int _hidden_size, int _kernel_size)
        {
            input_size = _input_size;
            hidden_size = _hidden_size;
            kernel_size = _kernel_size;
            input_sizeNUM = _input_size+ _hidden_size;
            output_sizenum = 4 * (_hidden_size);
            _padding = (kernel_size - 1) / 2;
            ConvGates = new convlayer(_stride, _padding, kernel_size, input_sizeNUM, output_sizenum);
 

        }
        Matrix[] in_gate; //输入门
        Matrix[] remember_gate;//记忆门
        Matrix[] out_gate;//输出门
        Matrix[] cell_gate;//单元门
        Matrix[] cell;
        Matrix[] tempall;
        public Matrix[] inputall { get; set; }
        SigmodLayer slingate=new SigmodLayer();
        SigmodLayer slinoutgate=new SigmodLayer(), slinremember_gate=new SigmodLayer();
        MulLayer mulingcell = new MulLayer();
        MulLayer mulremember_gate = new MulLayer();
        MulLayer hiddenmul = new MulLayer();
        TanhLayer tanhcell = new TanhLayer();
        TanhLayer tanhcell_gate = new TanhLayer();
       // Matrix[] prev_cell;
        public Matrix[] forward(Matrix[] input, Matrix[] prev_state)
        {
          
            if (prev_state == null)
            {
                prev_state = new Matrix[hidden_size];

                for (var b = 0; b < hidden_size; b++)
                    prev_state[b] = new Matrix(input[0].values.GetLength(0), input[0].values.GetLength(1));
             
                // +list(input.data.size()[2:])
            }


            // var prev_hidden = Matrix.chunk(prev_state, 2);
            // prev_cell = prev_hidden[1];
            //var prev_cell = Matrix.Clone( prev_state);
            // inputall = Matrix.cat(input, prev_hidden[0]);
            inputall = Matrix.cat(input, prev_state); 
             tempall = ConvGates.Forward(inputall);
            Matrix[][] TRg = Matrix.chunk(tempall, 4);
            in_gate = TRg[0];
            remember_gate = TRg[1];
            out_gate = TRg[2];
            cell_gate = TRg[3];

            in_gate =( slingate.forward(in_gate));
            remember_gate =(slinremember_gate.forward(remember_gate));
            out_gate =(slinoutgate.forward(out_gate));

            cell_gate =(tanhcell_gate.forward(cell_gate));
            var RPT = mulremember_gate.forward(remember_gate, prev_state);
            var ingcell = mulingcell.forward(in_gate, cell_gate);
            cell = Matrix.MatrixAdd(RPT, ingcell);
            //cell = (remember_gate * prev_cell) + (in_gate * cell_gate)
            //hidden = out_gate * f.tanh(cell)
            var tcell = tanhcell.forward(cell);
            var hidden = hiddenmul.forward(out_gate, tcell);
            //    return new Matrix[2][,] { cell, hidden };
            return hidden;
        }
        public Matrix[,] Backward(Matrix[] data, Matrix[] ytag)
        {

            MSELoss loss = new MSELoss();
            Matrix[,] Dgirdsum=new Matrix[ConvGates.inChannels, ConvGates.outChannels];
              Matrix[][] inttemp = Matrix.chunk(inputall, ConvGates.inChannels);
          
            int iincount = 0;
            for (var ini = 0; ini < inttemp.Length; ini++)
            {


                Matrix[] Dgird = null;

                DbackDelegate Dback = delegate (int indexx, int x, int y, float xdata, float ydata, float tdata)
                {
                    float bb = -2 * (xdata) * (ydata - tdata);
                    bb = slingate.backward(indexx, x, y, bb);
                    bb = mulingcell.backward(indexx, x, y, bb);
                    bb = tanhcell.backward(indexx, x, y, bb);
                    bb = hiddenmul.backwardY(indexx, x, y, bb);

                    return bb;
                };
                Matrix[] MEStemp = loss.MESBackwradconv(inttemp[ini], data, ytag, Dback, ConvGates.Kszie, ConvGates.stride, ConvGates.padding);
                Dgird = MEStemp;
                //---------------------------------------------


                DbackDelegate Dback2 = delegate (int indexx, int x, int y, float xdata, float ydata, float tdata)
                {
                    float bb = -2 * (xdata) * (ydata - tdata);

                    bb = slinremember_gate.backward(indexx, x, y, bb);
                    bb = mulremember_gate.backward(indexx, x, y, bb);
                    bb = tanhcell.backward(indexx, x, y, bb);
                    bb = hiddenmul.backwardY(indexx, x, y, bb);
                    return bb;
                };
                Matrix[] temp = loss.MESBackwradconv(inttemp[ini], data, ytag, Dback2,ConvGates.Kszie, ConvGates.stride, ConvGates.padding);

                //  temp = loss.MESDconv(temp, ConvGates.Kszie, ConvGates.stride, ConvGates.padding);
                Dgird = Matrix.cat(Dgird, temp);

                //---------------------------------------------
                DbackDelegate Dback3 = delegate (int indexx, int x, int y, float xdata, float ydata, float tdata)
                {
                    float bb = -2 * (xdata) * (ydata - tdata);


                    bb = slinoutgate.backward(indexx, x, y, bb);
                    bb = hiddenmul.backward(indexx, x, y, bb);
                    return bb;
                };
                temp = loss.MESBackwradconv(inttemp[ini], data, ytag, Dback3, ConvGates.Kszie, ConvGates.stride, ConvGates.padding);

                //  temp = loss.MESDconv(temp, ConvGates.Kszie, ConvGates.stride, ConvGates.padding);
                Dgird = Matrix.cat(Dgird, temp);
                //---------------------------------------------

                DbackDelegate Dback4 = delegate (int indexx, int x, int y, float xdata, float ydata, float tdata)
                {
                    float bb = -2 * (xdata) * (ydata - tdata);

                    bb = tanhcell_gate.backward(indexx, x, y, bb);
                    bb = mulingcell.backwardY(indexx, x, y, bb);
                    bb = tanhcell.backward(indexx, x, y, bb);
                    bb = hiddenmul.backwardY(indexx, x, y, bb);


                    return bb;
                };
                temp = loss.MESBackwradconv(inttemp[ini], data, ytag, Dback4, ConvGates.Kszie, ConvGates.stride, ConvGates.padding);

                // temp = loss.MESDconv(temp, ConvGates.Kszie, ConvGates.stride, ConvGates.padding);
                Dgird = Matrix.cat(Dgird, temp);
                //---------------------------------------------
                 for(var di=0;di< Dgird.Length;di++)
                    Dgirdsum[iincount, di] = Dgird[di];

                iincount++;
            }



            //cell_gateconv
            //  Dgird = Matrix.cat(Dgird, temloss, 1);

            return Dgirdsum;
            //  //temp = hiddenmul.backward(temp);


        }

        public void update(Matrix[,] DGRID, float learning_rate)
        {

            for (var i = 0; i < ConvGates.outChannels; i++)
            {
                float outsumbais = 0f;
                for (var j = 0; j < ConvGates.inChannels; j++)
                {
                    ConvGates.weights[j, i] = Matrix.MatrixSub(ConvGates.weights[j, i], Matrix.multiply(DGRID[j, i], learning_rate));
                    outsumbais += Matrix.sum(DGRID[j, i]);

                }
                ConvGates.basicData[i] = ConvGates.basicData[i] - (outsumbais * learning_rate);
            }
        }
    }
}
